package com.example.nonacademic_education.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.nonacademic_education.constant.Constant;
import com.example.nonacademic_education.constant.KapchaCode;
import com.example.nonacademic_education.constant.SingleFactory;
import com.example.nonacademic_education.dao.*;
import com.example.nonacademic_education.data.request.Graduate;
import com.example.nonacademic_education.data.request.LoginR;
import com.example.nonacademic_education.data.request.StudentExcelVo;
import com.example.nonacademic_education.data.request.StudentList;
import com.example.nonacademic_education.data.response.*;
import com.example.nonacademic_education.data.sys.Login;
import com.example.nonacademic_education.entity.Admin;
import com.example.nonacademic_education.entity.Department;
import com.example.nonacademic_education.entity.Student;
import com.example.nonacademic_education.entity.TheClass;
import com.example.nonacademic_education.service.StudentService;
import com.example.nonacademic_education.util.IpUtil;
import com.example.nonacademic_education.util.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;

/**
 * @Author: yanghua
 * @Date: 2023/4/2 11:18
 */
@Service
@Slf4j
public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> implements StudentService {
    @Resource
    private StudentDao studentDao;
    @Resource
    private DepartmentDao departmentDao;
    @Resource
    private TheClassDao theClassDao;
    @Resource
    private AnyConstantDao anyConstantDao;
    @Resource
    private KapchaCode kapchaCode;
    @Resource
    private IpUtil ipUtil;
    @Resource
    private ProjectDao projectDao;
    @Resource
    private AdminDao adminDao;

    @Override
    public R getInformFormID(String identityCardId, Long studentId) {
        //StudentDto student = studentDao.getOneStudent(identityCardId);
        List<GraduateInfoDto> diploma = studentDao.getDiplomaByCardId(identityCardId, studentId);
        if (diploma.isEmpty()) {
            return R.error("还未生成结业证书/没有该学员信息");
        }
        Map<String, Object> data = new HashMap<>();
        data.put("graduateInfo", diploma);
        data.put("total", diploma.size());
        data.put("headmaster", anyConstantDao.getHeadmaster());
        return R.ok(data, "查找成功");
    }

    @Override
    public R addStudent(StudentDto student) {
        //student.setPassword(BCrypt.hashpw(Constant.DEFAULT_PASSWORD, BCrypt.gensalt(4)));
        Boolean flag = studentDao.addOne(student);
        Map<String, Object> data = new HashMap<>();
        student.setClassName(theClassDao.getName(student.getClassId()));
        data.put("student", student);
        return flag ? R.ok(data, "添加成功") : R.error("添加失败");
    }

    @Override
    public R toGraduate(String certificateNo, String identityCardId) {
        LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Student::getIdentityCardId, identityCardId);
        StudentDto student = studentDao.getOneStudent(identityCardId);
        student.setCreationTime(new Date());
        student.setCertificateNo(certificateNo);
        student.setClassName(theClassDao.getName(student.getClassId()));
        studentDao.updateByidentityCardId(student);
        Map<String, Object> data = new HashMap<>();
        data.put("student", student);
        return R.ok(data, "修改信息成功");
    }

    @Override
    public R getStudentList(StudentList studentList) {
        if (studentList.getDepartmentId()==null){
            studentList.setDepartmentId(adminDao.getDepartmentIdByUsername(StpUtil.getLoginIdAsString()));
        }
        studentList.setPage((studentList.getPage() - 1) * studentList.getSize());
        List<StudentDto> sl = studentDao.getStudentList(studentList);
        Integer total = studentDao.getNumber(studentList);
        Map<String, Object> data = new HashMap<>();
        data.put("list", sl);
        data.put("total", total);
        return R.ok(data, "ok");
    }

    @Override
    public R deleteById(Long id) {
        studentDao.deleteStudent(id);
        return R.ok("删除成功");
    }

    @Override
    public R updateStudent(Student student) {
        studentDao.updateStudent(student);
        //StudentDto resStudent = studentDao.getOneStudent(student.getIdentityCardId());
        StudentDto resStudent = studentDao.getOneStudentById(student.getId());
        Map<String, Object> data = new HashMap<>();
        resStudent.setClassName(theClassDao.getName(resStudent.getClassId()));
        data.put("student", resStudent);
        return R.ok(data, "修改成功");
    }

    @Override
    public void getExcelTemplate(HttpServletResponse response) throws IOException {
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        List<StudentImportDto> data = new ArrayList<>();
        StudentImportDto studentImportDto = new StudentImportDto();
        studentImportDto.setName("张三");
        studentImportDto.setSex("男");
        studentImportDto.setIdentityCardId("02022863");
        studentImportDto.setCertificateNo("SP1063811220230001");
        data.add(studentImportDto);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("批量添加学员信息Excel模板", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), StudentImportDto.class).sheet("模板").doWrite(data);
    }

    @Override
    public R importStudents(MultipartFile file, Long classId, Long enrollmentTime1, Long graduateTime1) {
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        InputStream in = null;
        try {
            in = file.getInputStream();
        } catch (IOException e) {
            R.error("读取文件失败");
        }
        EasyExcel.read(in, StudentImportDto.class, new ReadListener<StudentImportDto>() {
            /**
             * 单次缓存的数据量
             */
            public static final int BATCH_COUNT = 20;
            /**
             *临时存储
             */
            private List<StudentImportDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(StudentImportDto data, AnalysisContext context) {
                //data.setPassword(data.getIdentityCardId().substring(12));
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 存储完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            /**
             * 加上存储数据库
             */
            private void saveData() {
                log.info("{}条数据，开始存储数据库！", cachedDataList.size());
                Date enrollmentTime = new Date(enrollmentTime1);
                Date graduateTime = new Date(graduateTime1);
                studentDao.addStudentBatch(cachedDataList, classId, enrollmentTime, graduateTime);
                log.info("存储数据库成功！");
            }
        }).sheet().doRead();
        return R.ok("导入学员信息成功");
    }

    @Override
    public R graduateStudents(Graduate graduate) {
        Integer n = studentDao.getClassStudentIdsNumber(graduate.getClassId());
        if (n == 0) {
            return R.error("该班级还没有学生信息");
        }
        Long projectId = theClassDao.getProjectIdById(graduate.getClassId());
        if (projectId == null) {
            //SingleFactory.getInstance().getIsUsed().put(graduate.getDepartmentId(), false);
            return R.error("获取培训项目信息失败");
        }
        Long departmentId = projectDao.getDepartmentIdById(projectId);
        if (departmentId == null) {
            //SingleFactory.getInstance().getIsUsed().put(graduate.getDepartmentId(), false);
            return R.error("获取办学单位信息失败");
        }
        graduate.setDepartmentId(departmentId);
        Boolean flag = SingleFactory.getInstance().getIsUsed().get(graduate.getDepartmentId());
        if (flag != null && flag) {
            return R.error("系统拥挤，请稍后再试");
        }
        SingleFactory.getInstance().getIsUsed().put(graduate.getDepartmentId(), true);
        //业务实现
        List<Graduate> graduates = new ArrayList<>();
        Department department = null;
//        if (graduate.getDepartmentId() == 0L) {
//            department = departmentDao.getOneByClassId(graduate.getClassId());
//        } else {
        //System.out.println(graduate.getDepartmentId());
        department = departmentDao.getOneById(graduate.getDepartmentId());
//        }
        if (department == null) {
            SingleFactory.getInstance().getIsUsed().put(graduate.getDepartmentId(), false);
            return R.error("获取办学单位编号失败");
        }
        theClassDao.updateGraduateInfo(graduate.getTrainingInfo(), graduate.getClassId());
        List<Long> idsNoGraduate = studentDao.getClassStudentIdsNoGraduate(graduate.getClassId());
        Integer isGraduateNumber = studentDao.getGraduatedNumber(graduate.getClassId());
        if (idsNoGraduate.size() > 0) {
            for (int i = 0; i < idsNoGraduate.size(); i++) {
                Graduate g = new Graduate();
                g.setId(idsNoGraduate.get(i));
                g.setCertificateNo("SP10638" + department.getCoding() + graduate.getYear() + String.format("%04d", department.getCertificateNo() + i));
                graduates.add(g);
            }
            // 当前年份才更改
            // 获取当前日期
            Calendar calendar = Calendar.getInstance();
            int year = calendar.get(Calendar.YEAR);
            if (graduate.getYear()==year) {
                departmentDao.updateCertificateNo(department.getCertificateNo() + idsNoGraduate.size(), department.getId());
            }
            studentDao.updateGraduateInfo(graduate.getCreationTime(), graduates);
        }
        //System.out.println(isGraduateNumber);
        if (isGraduateNumber > 0) {
            studentDao.updateCreationTime(graduate.getYear(), graduate.getCreationTime(), graduate.getClassId());
        }
        SingleFactory.getInstance().getIsUsed().put(graduate.getDepartmentId(), false);
        return R.ok("添加该班级的结业信息成绩成功");
    }

    @Override
    public R getGraduateInfo(Long classId) {
        Integer no = studentDao.getNoGraduateInfo(classId);
        if (no > 0) {
            return R.error("该班级有学员未生成结业信息，无法批量导出");
        }
        Map<String, Object> data = new HashMap<>();
        data.put("graduateInfo", studentDao.getGraduateInfo(classId));
        data.put("total", studentDao.countGraduateInfo(classId));
        data.put("headmaster", anyConstantDao.getHeadmaster());
        TheClass theClass = theClassDao.getOneById(classId);
        data.put("trainingInfo", theClass.getTrainingInfo());
        data.put("className", theClass.getName());
        return R.ok(data, "获取数据成功");
    }

    @Override
    public R login(LoginR admin, HttpServletRequest request) {
        StudentDto st=studentDao.getOneStudent(admin.getUsername());
        if (st==null){
            return R.error("您输入的账号不存在！");
        }
        if (st.getAble()==0){
            boolean disable = StpUtil.isDisable(admin.getUsername());
            if (disable) {
                long disableTime = StpUtil.getDisableTime(admin.getUsername())/60;
                return R.error("您的账号已被系统自动封禁，剩余时间 "+disableTime/60+" 小时 "+disableTime%60+" 分钟，请联系管理员处理！");
            }
            return R.error("您的账号已被管理员永久封禁，请联系管理员处理！");
        }
        if (admin.getCode() == null) {
            return R.error("请输入验证码");
        }
//        String ip = ipUtil.getIpAddr(request);
//        if (ip.equals("")) {
//            return R.error("请关闭代理");
//        }
        if (kapchaCode.getCodes().get(admin.getCode())==null) {
            return R.error("验证码错误或已过期");
        }
//        if (Constant.DEFAULT_PASSWORD.equals(admin.getPassword())) {
//            Student stu = studentDao.getOneByUsernameAndPassword1(admin.getUsername(), admin.getPassword());
//            if (stu != null) {
//                //studentDao.resetPassword(stu.getIdentityCardId(),stu.getPassword());
//                return R.error("账号或密码错误");
//            }
//        }
        String password=studentDao.getPasswordByUsername(admin.getUsername());
        boolean result = BCrypt.checkpw(admin.getPassword(),password);
        if (!result) {
            //累加错误次数
            Login login = SingleFactory.getInstance().getLogins().get(admin.getUsername());
            if (login==null){
                login=new Login();
                login.setCnt(1);
                login.setUsername(admin.getUsername());
            }else {
                if (login.getCnt()>=5){
                    SingleFactory.getInstance().getLogins().remove(admin.getUsername());
                    // 先踢下线
                    StpUtil.kickout(admin.getUsername());
                    // 再封禁账号,86400是1天
                    StpUtil.disable(admin.getUsername(), 86400/4);
                    enableAccount(admin.getUsername(), 0);
                    login.setCnt(-1);
                    SingleFactory.getInstance().getLogins().put(admin.getUsername(), login);
                    return R.error("错误次数太多，您的账号已被系统封禁！");
                }
                if(login.getLoginTime()-System.currentTimeMillis()<30000L){
                    login.setCnt(login.getCnt()+1);
                }
            }
            login.setLoginTime(System.currentTimeMillis());
            SingleFactory.getInstance().getLogins().put(admin.getUsername(), login);
            return R.error("账号或密码错误");
        }
        StudentUserDto student;// = studentDao.getOneByUsernameAndPassword(admin.getUsername(), admin.getPassword());
        student = studentDao.getOneByUsername(admin.getUsername());
        if (student.getIdentificationPhotoUrl() == null) {
            StudentUserDto s = studentDao.getOneHaveUrl(admin.getUsername());
            if (s != null) {
                studentDao.updateUrlByUsername(s.getIdentityCardId(), s.getIdentificationPhotoUrl());
                student.setIdentificationPhotoUrl(s.getIdentificationPhotoUrl());
            }
        }
        StpUtil.login(admin.getUsername());
        Map<String, Object> data = new HashMap<>();
        student.setAuthority(Constant.STUDENT);
        data.put("user", student);
        data.put("token", StpUtil.getTokenValue());
        kapchaCode.getCodes().remove(admin.getCode());
        return R.ok(data, "登录成功");
    }

    @Override
    public R uploadHeadImage(Student student) {
        if (studentDao.uploadHeadImage(student.getIdentityCardId(), student.getIdentificationPhotoUrl()) > 0) {
            return R.ok("设置证件照成功");
        } else {
            return R.error("设置证件照失败");
        }
    }

    @Override
    public R resetPassword(Student student) {
        student.setPassword(BCrypt.hashpw(student.getPassword(), BCrypt.gensalt(4)));
        if (studentDao.resetPassword(student.getIdentityCardId(), student.getPassword()) > 0) {
            return R.ok("修改密码成功");
        } else {
            return R.error("修改密码失败");
        }
    }

    @Override
    public R uploadIdentificationPhoto(MultipartFile file) {
        String pre = Constant.proBiologyImagePath + Constant.IdentificationPhotoPath;
        File dir = new File(pre);
        if (!dir.exists()) {
            if (!dir.mkdir()) {
                return R.error("创建文件夹失败");
            }
        }
        String filename = System.currentTimeMillis() % 10000 + file.getOriginalFilename();
        if (file.getOriginalFilename().equals("blob")){
            filename=System.currentTimeMillis() % 10000+StpUtil.getLoginIdAsString()+".jpg";
        }
        pre += filename;
        File newFile = new File(pre);
        try {
            file.transferTo(newFile);
        } catch (IOException e) {
            e.printStackTrace();
            return R.error("文件'" + filename + "'上传失败");
        }
        Map<String, Object> data = new HashMap<>();
        data.put("url", Constant.SERVE_URL + Constant.GET_FILE_URL + Constant.IdentificationPhotoPath + filename);
        return R.ok(data, "上传成功");
    }

    @Override
    public void exportStatisticsExcel(StudentExcelVo studentExcelVo, HttpServletResponse response) throws IOException {
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        Integer no = studentDao.getNoGraduateInfo(studentExcelVo.getClassId());
        if (no > 0) {
            return;
        }
        List<StudentExcelDto> data = studentDao.exportStatisticsExcel(studentExcelVo);
        //System.out.println(data.get(0));
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String filename;
        if (studentExcelVo.getClassId() != null) {
            filename = theClassDao.getName(studentExcelVo.getClassId());
        } else {
            filename = projectDao.getNameById(studentExcelVo.getProjectId());
            ;
        }
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode(filename + "学员结业信息", "UTF-8").replaceAll("\\+", "%20");
        //response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), StudentExcelDto.class).sheet("数据").doWrite(data);
    }

    @Override
    public R importStudentsGraduated(MultipartFile file, Long classId, Long enrollmentTime1, Long graduateTime1, String trainingInfo, Long creationTime1) {
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        InputStream in = null;
        try {
            in = file.getInputStream();
        } catch (IOException e) {
            R.error("读取文件失败");
        }
        EasyExcel.read(in, StudentImportDto.class, new ReadListener<StudentImportDto>() {
            /**
             * 单次缓存的数据量
             */
            public static final int BATCH_COUNT = 20;
            /**
             *临时存储
             */
            private List<StudentImportDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(StudentImportDto data, AnalysisContext context) {
                //data.setPassword(data.getIdentityCardId().substring(12));
//                if (data.getIdentityCardId()!=null) {
//                    data.setIdentityCardId(data.getIdentityCardId().substring(data.getIdentityCardId().length()-6));
//                }//放到数据库left了
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 存储完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }

            /**
             * 加上存储数据库
             */
            private void saveData() {
                log.info("{}条数据，开始存储数据库！", cachedDataList.size());
                Date enrollmentTime = new Date(enrollmentTime1);
                Date graduateTime = new Date(graduateTime1);
                Date creationTime=new Date(creationTime1);
                studentDao.addStudentBatchGraduted(cachedDataList, classId, enrollmentTime, graduateTime,creationTime);
                log.info("存储数据库成功！");
            }
        }).sheet().doRead();
        //update class
        theClassDao.addTrainingInfoByClassId(trainingInfo,classId);
        //update department
        // 当前年份才更改
        // 获取当前日期
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        Integer noYear=projectDao.getYearByClassId(classId);
        if (noYear==null){
            noYear=year;
        }
        if (year==noYear){
            Integer currentMaxNo=departmentDao.getMaxNoByClassId(classId);
            String maxNoThisClassString=studentDao.getMaxNoThisClass(classId);
            if (maxNoThisClassString!=null&&currentMaxNo!=null){
                System.out.println(maxNoThisClassString);
                Integer maxNoThisClass=Integer.parseInt(maxNoThisClassString);
                if (maxNoThisClass>currentMaxNo){
                    departmentDao.updateNoByClassId(maxNoThisClass,classId);
                }
            }
        }
        return R.ok("导入学员信息成功");
    }

    @Override
    public R resetStudentPassword(String identityCardId) {
        if (studentDao.resetPassword(identityCardId,BCrypt.hashpw(Constant.DEFAULT_PASSWORD, BCrypt.gensalt(4)))>0){
            return R.ok("重置学员密码成功!");
        }else {
            return R.error("重置学员密码失败!");
        }
    }

    @Override
    public R enableAccount(String id, Integer able) {
        studentDao.enableAccount(id, able);
        if (able==1){
            if (StpUtil.isDisable(id)){
                StpUtil.untieDisable(id);
            }
            return R.ok("解封账号成功");
        }else {
            return R.ok("封禁账号成功");
        }
    }
}


//    @Override
//    public void getExcelTemplate(HttpServletResponse response) throws IOException {
//        File file = new File(Constant.proBiologyImagePath + "添加学员信息--Excel模板.xlsx");
//        //File file = new File("E:/flash/添加学员信息--Excel模板.xlsx");
//        Long length = file.length();
//        response.addHeader("Content-Length", String.valueOf(length));
//        //下载形式，一般跟application/octet-stream一起使用，设置Content-Disposition为attachment
//        response.addHeader("Content-Disposition",
//                "attachment;filename=" + URLEncoder.encode("添加学员信息--Excel模板.xlsx", "utf-8"));
//        //表示以二进制流的形式返回
//        response.setContentType("application/octet-stream");
//        OutputStream outputStream = response.getOutputStream();
//        byte[] buff = new byte[1024];
//        BufferedInputStream bis;
//        FileInputStream inputStream = new FileInputStream(file);
//        bis = new BufferedInputStream(inputStream);
//        int i = bis.read(buff);
//        while (i != -1) {
//            outputStream.write(buff, 0, buff.length);
//            outputStream.flush();
//            i = bis.read(buff);
//        }
//        bis.close();
//        outputStream.close();
//    }
